home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / UTILITY / ANSI13.ARJ / ANSI.ASM next >
Assembly Source File  |  1989-08-10  |  67KB  |  1,357 lines

  1. ;------------------------------------------------------------------------;
  2. ;  ANSI.COM - Replacement for the ANSI.SYS console device driver.        ;
  3. ;  Unlike ANSI.SYS which must be installed at boot time, ANSI.COM        ;
  4. ;  can be installed and uninstalled at anytime.  Enhancements include    ;
  5. ;  a fast screen write and variable sized keyboard reassignment buffer.  ;
  6. ;                                                                        ;
  7. ;  Update 3/2/89 - Fix for DOS function 0Bh, Check Standard Input Status ;
  8. ;                      and STDIN in ANSI_INT_21 handler.                 ;
  9. ;                  Leading zero inserted for Device Status Report for    ;
  10. ;                      single digit cursor positions.                    ;
  11. ;                  INFORMATION typo 40 for 40H.                          ;
  12. ;                  WRITE_FAST modified to handle CR and LF instead of    ;
  13. ;                      WRITE_CHAR.                                       ;
  14. ;  Update 3/7/89 - Fix for CLS in graphics mode.                         ;
  15. ;                                                                        ;
  16. ;  Update 8/8/89 - STI added to INT 21 and 29 handler.                   ;
  17. ;                                                                        ;
  18. ;  PC Magazine - Michael J. Mefford                                      ;
  19. ;------------------------------------------------------------------------;
  20.  
  21. _TEXT          SEGMENT PUBLIC 'CODE'
  22.                ASSUME  CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
  23.                ORG     100H
  24. START:         JMP     INITIALIZE
  25.  
  26. ;                 DATA AREA
  27. SIGNATURE      DB      CR,SPACE,SPACE,SPACE,CR,LF
  28. COPYRIGHT      DB      "ANSI 1.3 (C) 1988 Ziff Communications Co.",CR,LF
  29. PROGRAMMER     DB      "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$"
  30.                DB      CTRL_Z
  31.  
  32. CR             EQU     13
  33. LF             EQU     10
  34. CTRL_Z         EQU     26
  35. SPACE          EQU     32
  36. BOX            EQU     254
  37. ESC_CHAR       EQU     27
  38. SINGLE_QUOTE   EQU     39
  39. DOUBLE_QUOTE   EQU     34
  40. BELL           EQU     7
  41. BS             EQU     8
  42. TAB            EQU     9
  43.  
  44. OFF            EQU     1
  45. ON             EQU     2
  46. SLOW           EQU     4
  47. FAST           EQU     8
  48. STATUS_MASK    EQU     11111100B
  49.  
  50. ANSI_STATE     DW      ESC_STATE
  51. STATUS         DB      ON OR FAST
  52. PARAMETERS     DB      "OFF ON  SLOWFAST"
  53. LAST_PARAMETER EQU     $ - PARAMETERS
  54.  
  55. OLD_INT_29     DW      ?,?
  56. OLD_INT_16     DW      ?,?
  57. OLD_INT_21     DW      ?,?
  58.  
  59. ATTRIBUTE      DB      7
  60. SAVE_POSITION  DW      0
  61. LINE_WRAP      DB      ON
  62. QUOTE_TYPE     DB      ?
  63. ESC_COUNT      DW      0
  64. NUMBER_COUNT   DW      0
  65.  
  66. COMMAND_TABLE  LABEL   BYTE
  67. DB   "H", "A", "B", "C", "D", "f", "n", "s", "u", "K", "m", "h", "l", "p", "J"
  68. COMMAND_LENGTH EQU     $ - COMMAND_TABLE
  69.  
  70. DW   CURS_POSITION, CURSOR_UP,     CURSOR_DOWN, CURS_FORWARD, CURS_BACKWARD
  71. DW   HORZ_VERT_POS, DEVICE_STATUS, SAVE_CURSOR, RESTORE_CURS, ERASE_IN_LINE
  72. DW   SGR,           SET_MODE,      RESET_MODE,  REASSIGNMENT, CLS
  73. COMMAND_END    EQU     $ - 2
  74.  
  75. ATTRIBUTE_TABLE        LABEL    BYTE
  76. DB   00,01,04,05,07,08,30,31,32,33,34,35,36,37,40,41,42,43,44,45,46,47
  77. ATTRIBUTE_LENGTH       EQU      $ - ATTRIBUTE_TABLE
  78.  
  79. ;Format: AND mask,OR mask
  80. DB      000H,07H, 0FFH,08H, 0F8H,01H, 0FFH,80H, 0F8H,70H, 088H,00H
  81. DB      0F8H,00H, 0F8H,04H, 0F8H,02H, 0F8H,06H, 0F8H,01H, 0F8H,05H
  82. DB      0F8H,03H, 0F8H,07H, 08FH,00H, 08FH,40H, 08FH,20H, 08FH,60H
  83. DB      08FH,10H, 08FH,50H, 08FH,30H, 08FH,70H
  84. ATTRIBUTE_END          EQU      $ - 2
  85.  
  86. BIOS_ACTIVE_PAGE       EQU     62H
  87. ACTIVE_PAGE    DB      ?
  88. ADDR_6845      DW      ?
  89. BIOS_CRT_MODE          EQU     49H
  90. CRT_MODE       DB      ?
  91. CRT_COLS       DW      ?
  92. CRT_LEN        DW      ?
  93. CRT_START      DW      ?
  94. CRT_DATA_LENGTH        EQU     $ - CRT_MODE
  95. CURSOR_POSN    LABEL   WORD
  96. CURSOR_COL     DB      ?
  97. CURSOR_ROW     DB      ?
  98. CRT_ROWS       DB      ?
  99.  
  100. DOS_INPUT      DB      OFF
  101. BUSY_FLAG      DB      OFF
  102. REASSIGN_FLAG  DB      OFF
  103. REMOVE_FLAG    DB      ON
  104. REASSIGN_COUNT DW      0
  105. REASSIGN_POS   DW      ?
  106. FUNCTION_16    DB      ?
  107. ZR             EQU     1000000B
  108.  
  109. ESC_BUFFER_SIZE      EQU     126
  110. REASSIGNMENT_DEFAULT EQU     200
  111. REASSIGNMENT_SIZE    DW      REASSIGNMENT_DEFAULT
  112. REASSIGNMENT_MAX     EQU     60 * 1024
  113. REASSIGN_END         DW      REASSIGNMENT_BUFFER
  114. BUFFER_END           DW      REASSIGNMENT_BUFFER + REASSIGNMENT_DEFAULT
  115.  
  116. ;                   CODE AREA
  117. ;************* INTERRUPT HANDLERS *************;
  118. ;------------------------------------------------------------------------------;
  119. ; INT 29 is an undocumented interrupt called by DOS for output to the console. ;
  120. ;------------------------------------------------------------------------------;
  121.  
  122. ANSI_INT_29    PROC    FAR
  123.                STI
  124.                PUSH    AX                      ;Save all registers.
  125.                PUSH    BX
  126.                PUSH    CX
  127.                PUSH    DX
  128.                PUSH    SI
  129.                PUSH    DI
  130.                PUSH    DS
  131.                PUSH    ES
  132.                PUSH    BP
  133.  
  134.                CLD                             ;All string operations forward.
  135.                MOV     BX,CS                   ;Point to our data segment.
  136.                MOV     DS,BX
  137.                MOV     ES,BX
  138.                CALL    ANSI_STATE              ;Call the current state of
  139.                                                ; the ANSI Esc sequence.
  140.                POP     BP
  141.                POP     ES
  142.                POP     DS
  143.                POP     DI
  144.                POP     SI
  145.                POP     DX
  146.                POP     CX
  147.                POP     BX
  148.                POP     AX                      ;Restore all registers and
  149.                IRET                            ; return.
  150. ANSI_INT_29    ENDP
  151.  
  152. ;------------------------------------------------;
  153.  
  154. ANSI_INT_21    PROC    FAR
  155.                PUSHF
  156.                CMP     AH,0BH                  ;If DOS function 0Bh (Check
  157.                JNZ     CK_INPUT                ; Standard Input Status)
  158.                CMP     CS:REASSIGN_FLAG,ON     ; and reassignment in progress,
  159.                JNZ     CK_INPUT                ; including a Device Status
  160.                CALL    DWORD PTR CS:OLD_INT_21 ; Request, then process call in
  161.                MOV     AL,0FFH                 ; case control break pressed;
  162.                IRET                            ; then return 0FFh for char ready
  163.  
  164. CK_INPUT:      CMP     AH,3FH                  ;Read from STDIN?
  165.                JNZ     CK_CONSOLE
  166.                OR      BX,BX
  167.                JZ      INPUT
  168.  
  169. CK_CONSOLE:    CMP     AH,0AH                  ;If DOS Int 21 functions Ah,
  170.                JZ      INPUT                   ; 7h, 1h or console input (6h)
  171.                CMP     AH,7                    ; then tell INT 16, DOS input
  172.                JZ      INPUT                   ; is active.
  173.                CMP     AH,1
  174.                JZ      INPUT
  175.                CMP     AH,8
  176.                JZ      INPUT
  177.                CMP     AH,6
  178.                JNZ     QUICK21_EXIT
  179.                CMP     DL,0FFH
  180.                JZ      INPUT
  181. QUICK21_EXIT:  POPF                              ;If not, let the original
  182.                JMP     DWORD PTR CS:OLD_INT_21   ; interrupt process the call.
  183.  
  184. INPUT:         POPF
  185.                MOV     CS:DOS_INPUT,ON           ;INT 16 handler flag.
  186.                PUSHF
  187.                CALL    DWORD PTR CS:OLD_INT_21   ;Emulate an interrupt.
  188.                MOV     CS:DOS_INPUT,OFF          ;Turn flag back off.
  189.                STI
  190.                RET     2                         ;Return with current flags.
  191. ANSI_INT_21    ENDP
  192.  
  193. ;-----------------------------------------------------------------;
  194. ; If we get here via a DOS console input call, any awaiting key   ;
  195. ; in keyboard buffer is checked to see if it is to be reassigned. :
  196. ;-----------------------------------------------------------------;
  197.  
  198. ANSI_INT_16    PROC    FAR
  199.                STI                             ;Interrupts back on.
  200.                PUSHF                           ;Preserve flags on stack.
  201.                PUSH    BP
  202.                PUSH    DS
  203.                PUSH    AX
  204.                PUSH    CS                      ;Point to our data.
  205.                POP     DS
  206.  
  207.                CMP     DOS_INPUT,OFF           ;If not called via a DOS
  208.                JZ      QUICK16_EXIT            ; console input call, exit.
  209.                CMP     BUSY_FLAG,ON            ;If already processing a call,
  210.                JZ      QUICK16_EXIT            ; exit.
  211.                CLD                             ;All string operations forward.
  212.                MOV     BP,SP                   ;Base reference to flags on stack
  213.                MOV     FUNCTION_16,AH          ;Save function request.
  214.                AND     AH,NOT 10H              ;Strip possible extended request.
  215.                JZ      GET_KEY                 ;If zero, it's wait for a key.
  216.                DEC     AH                      ;Else, decrement.  If zero it's
  217.                JZ      KEY_STATUS              ; key status, else exit.
  218.  
  219. QUICK16_EXIT:  POP     AX                      ;Restore registers and let
  220.                POP     DS                      ; original interrupt handler
  221.                POP     BP                      ; process the call.
  222.                POPF
  223.                JMP     DWORD PTR CS:OLD_INT_16
  224.  
  225. KEY_STATUS:    OR      BYTE PTR [BP+6],ZR      ;Assume none ready; ZR = 1.
  226. GET_KEY:       MOV     BUSY_FLAG,ON            ;Non-reentrant; flag busy.
  227.                POP     AX
  228.                PUSH    BX                      ;Save some more registers.
  229.                PUSH    CX
  230.                PUSH    DX
  231.                PUSH    SI
  232.                PUSH    DI
  233.                CMP     REASSIGN_FLAG,ON        ;Already in process of reassign?
  234.                JZ      CK_BUFFER               ;If yes, check kbd buffer.
  235.  
  236.                PUSHF                           ;Else, emulate an interrupt.
  237.                CALL    DWORD PTR OLD_INT_16
  238.                PUSHF                           ;Status call results in flags.
  239.                TEST    FUNCTION_16,1           ;Was it a status call?
  240.                JZ      CK_DUP                  ;If no, check key returned.
  241.                POPF                            ;Else, retrieve status results.
  242.                JZ      INT16_EXIT              ;If zero, no key waiting.
  243.                AND     BYTE PTR [BP+6],NOT ZR  ;Else, ZR = 0.
  244.                PUSHF                           ;PUSHF just to keep stack right.
  245.  
  246. CK_DUP:        POPF                            ;Fix stack.
  247.                MOV     BX,AX                   ;Match procedure wants key in BX.
  248.                CALL    CK_MATCH                ;Check reassignment buffer.
  249.                JC      INT16_EXIT              ;If no match, exit.
  250.                MOV     AX,[DI]                 ;Else, retrieve string length.
  251.                SUB     AX,3                    ;Adjust for length word and match
  252.                ADD     DI,3                    ; byte; same for string pointer.
  253.                OR      BL,BL                   ;Extended key? ie. key = 0.
  254.                JNZ     STORE_COUNT             ;If no, save length and pointer.
  255.                DEC     AX                      ;Else, adjust for extended code.
  256.                INC     DI
  257. STORE_COUNT:   MOV     REASSIGN_COUNT,AX       ;Save string length.
  258.                MOV     REASSIGN_POS,DI         ;Save pointer to string.
  259.                MOV     REASSIGN_FLAG,ON        ;Flag that replacement in effect.
  260. CK_BUFFER:     TEST    FUNCTION_16,1           ;Was it a key wait function call?
  261.                JNZ     RETRIEVE                ;If no, key status; get it.
  262.                CMP     REMOVE_FLAG,OFF         ;Removed it from kbd buffer?
  263.                JZ      RETRIEVE                ;If yes, get reassignment.
  264.                MOV     AH,FUNCTION_16          ;Else, eat the replaced character
  265.                INT     16H                     ; via INT 16.
  266.                MOV     REMOVE_FLAG,OFF         ;Flag character eaten.
  267.  
  268. RETRIEVE:      MOV     DI,REASSIGN_POS         ;Retrieve pointer to string.
  269.                MOV     AX,[DI]                 ;Retrieve replacement character.
  270.                TEST    FUNCTION_16,1           ;Is it a key wait function call?
  271.                JZ      REMOVE                  ;If yes, bump pointer up one.
  272.                AND     BYTE PTR [BP+6],NOT ZR  ;If no, status call; ZR = 0
  273.                JMP     SHORT INT16_EXIT        ;All done.
  274.  
  275. REMOVE:        INC     REASSIGN_POS            ;Move pointer to next character.
  276.                DEC     REASSIGN_COUNT          ;One less character to replace.
  277.                JZ      REASSIGN_DONE           ;If end of string, reset flags.
  278.                OR      AL,AL                   ;Else, extended replacement?
  279.                JNZ     INT16_EXIT              ;If no, done here.
  280.                INC     REASSIGN_POS            ;Else adjust pointers.
  281.                DEC     REASSIGN_COUNT
  282.                JNZ     INT16_EXIT              ;End of string?
  283. REASSIGN_DONE: MOV     REASSIGN_FLAG,OFF       ;If yes, reset flags.
  284.                MOV     REMOVE_FLAG,ON
  285.  
  286. INT16_EXIT:    MOV     BUSY_FLAG,OFF           ;Reset the busy flag.
  287.                POP     DI                      ;Restore registers.
  288.                POP     SI
  289.                POP     DX
  290.                POP     CX
  291.                POP     BX
  292.                POP     DS
  293.                POP     BP
  294.                POPF                            ;Flags on stack have status
  295.                RET     2                       ; results; kill old flags.
  296. ANSI_INT_16    ENDP
  297.  
  298. ;************* ANSI ESCAPE STATE ROUTINES ************* ;
  299.  
  300. ESC_STATE:     MOV     BX,OFFSET BRACKET_STATE ;Assume Esc character.
  301.                CMP     AL,ESC_CHAR             ;Is it Esc?  If yes, store
  302.                JZ      SHORT_JUMP              ; char. and next state.
  303.                JMP     WRITE_CHAR              ;Else, normal char; write it.
  304.  
  305. ;------------------------------------------------;
  306.  
  307. BRACKET_STATE: MOV     BX,OFFSET SPECIAL_STATE ;Assume left bracket.
  308.                CMP     AL,"["                  ;Is it left bracket?  If yes,
  309. SHORT_JUMP:    JZ      STORE_STATE             ;store char. and next state.
  310.                JMP     FLUSH_BUFFER            ;Else, flush Esc out of buffer.
  311.  
  312. ;---------------------------------------------------------------;
  313. ; Must process <ESC>[2J (CLS) regardless if ANSI.COM ON or OFF. ;
  314. ;---------------------------------------------------------------;
  315.  
  316. SPECIAL_STATE: MOV     BX,OFFSET CLS_STATE     ;Assume Erase in Display code.
  317.                CMP     AL,"2"                  ;Is it the "2" ?
  318.                JZ      STORE_STATE             ;If yes, progress to next state.
  319.                TEST    STATUS,OFF              ;Else, is ANSI OFF ?
  320.                JNZ     FLUSH_BUFFER            ;If yes, flush Esc, bracket.
  321.                MOV     BX,OFFSET PARAM_STATE   ;Else, check for first Set,
  322.                CMP     AL,"="                  ; Reset Mode characters of
  323.                JZ      STORE_STATE             ; "=" and "?".
  324.                CMP     AL,"?"
  325.                JZ      STORE_STATE             ;If found, store.
  326.                JMP     SHORT DO_PARAMETER      ;Else, check other codes.
  327.  
  328. ;------------------------------------------------;
  329.  
  330. CLS_STATE:     CMP     AL,"J"                  ;Is it second character of CLS?
  331.                MOV     DI,OFFSET COMMAND_END   ;If yes, execute
  332.                JZ      EXECUTE                 ; Erase in Display procedure.
  333.                TEST    STATUS,OFF              ;ANSI OFF?  If yes, flush buffer.
  334.                JNZ     FLUSH_BUFFER            ; If not fall through to process.
  335.                MOV     BYTE PTR NUMBER_BUFFER,2   ;Store the previous 2.
  336. DO_PARAMETER:  MOV     ANSI_STATE,OFFSET PARAM_STATE  ;Parameter state.
  337.  
  338. ;------------------------------------------------;
  339.  
  340. PARAM_STATE:   CALL    CK_QUOTE                ;Is it single or double quotes?
  341.                JZ      STORE_STATE             ;If yes, store string state.
  342.                CMP     AL,";"                  ;Is it semi-colon delimiter?
  343.                JNZ     CK_NUMBER               ;If no, check if number.
  344.                INC     NUMBER_COUNT            ;Else, increment number count.
  345.                JMP     SHORT BUFFER_CHAR       ;Buffer the semi-colon.
  346.  
  347. CK_NUMBER:     CMP     AL,"0"                  ;Is it below 0 ?
  348.                JB      FLUSH_BUFFER            ;If yes, illegal; flush buffer.
  349.                CMP     AL,"9"                  ;Else, is it above 9 ?
  350.                JA      DO_COMMAND              ;If yes, check for command char.
  351.                CALL    ACCUMULATE              ;Else it's a number; accumulate.
  352.                JMP     SHORT BUFFER_CHAR       ;Buffer the character.
  353.  
  354. DO_COMMAND:    MOV     DI,OFFSET COMMAND_TABLE ;Point to legal ANSI commands.
  355.                MOV     CX,COMMAND_LENGTH       ;Number of commands.
  356.                REPNZ   SCASB                   ;Check for a match.
  357.                JNZ     FLUSH_BUFFER            ;If no match, flush sequence.
  358.                INC     NUMBER_COUNT            ;Else, increment for last number.
  359.                MOV     DI,OFFSET COMMAND_END   ;Point to appropriate command
  360.                SHL     CX,1                    ; processing procedure.
  361.                SUB     DI,CX
  362. EXECUTE:       CALL    GET_BIOS_DATA           ;Get cursor position data.
  363.                CALL    DS:[DI]                 ;Do command subroutine.
  364.                JMP     SHORT FLUSH_END         ;Clear buffer.
  365.  
  366. ;------------------------------------------------;
  367.  
  368. QUOTE_STATE:   CMP     AL,QUOTE_TYPE           ;Is it an ending string quote?
  369.                JNZ     BUFFER_CHAR             ;If no, buffer literal.
  370.                MOV     BX,OFFSET PARAM_STATE   ;Else, back to parameter parsing.
  371.  
  372. ;------------------------------------------------;
  373.  
  374. STORE_STATE:   MOV     ANSI_STATE,BX           ;Store the ANSI state.
  375.  
  376. ;------------------------------------------------;
  377.  
  378. BUFFER_CHAR:   MOV     DI,ESC_COUNT            ;Buffer the character in case
  379.                CMP     DI,ESC_BUFFER_SIZE      ; ending ANSI command is illegal.
  380.                JZ      FLUSH_BUFFER            ;If buffer full, flush.
  381.                ADD     DI,OFFSET ESC_BUFFER    ;Point to next buffer position.
  382.                STOSB                           ;Store the character.
  383.                INC     ESC_COUNT               ;Increment the sequence count.
  384.                RET
  385.  
  386. ;------------------------------------------------;
  387.  
  388. FLUSH_BUFFER:  PUSH    AX                      ;Save the current character.
  389.                MOV     SI,OFFSET ESC_BUFFER    ;Point to the sequence buffer.
  390.                MOV     CX,ESC_COUNT            ;Count of buffered characters.
  391. NEXT_FLUSH:    LODSB                           ;Retrieve one.
  392.                PUSH    CX                      ;Save counter and pointer.
  393.                PUSH    SI
  394.                CALL    WRITE_CHAR              ;Write character to screen.
  395.                POP     SI                      ;Restore counter and pointer.
  396.                POP     CX
  397.                LOOP    NEXT_FLUSH              ;Flush entire buffer.
  398.                POP     AX                      ;Retrieve last character.
  399.                CALL    WRITE_CHAR              ;Write it also.
  400. FLUSH_END:     MOV     ESC_COUNT,0                   ;Reset counter.
  401.                MOV     ANSI_STATE,OFFSET ESC_STATE   ;Back to Esc state.
  402.                MOV     NUMBER_COUNT,0                ;Reset parameter counter.
  403.                PUSH    CS                            ;Point to our data.
  404.                POP     ES
  405.                MOV     DI,OFFSET NUMBER_BUFFER       ;Clear number buffer
  406.                MOV     CX,ESC_BUFFER_SIZE / 2        ; to zeros.
  407.                XOR     AX,AX
  408.                REP     STOSW
  409.                RET
  410.  
  411. ;------------------------------------------------;
  412. ; Slow video writes are via BIOS Write TTY.      ;
  413. ;------------------------------------------------;
  414.  
  415. WRITE_CHAR:    CMP     AL,BELL                 ;Let BIOS process BS and BELL.
  416.                JZ      WRITE_TTY
  417.                CMP     AL,BS
  418.                JZ      WRITE_TTY
  419.  
  420.                CALL    GET_BIOS_DATA           ;Get BIOS video data.
  421.                CMP     AL,TAB                  ;Is character a TAB?
  422.                JNZ     CK_ACTIVE               ;If no, process normally.
  423.                MOV     CX,CURSOR_POSN          ;Else, expand TAB to
  424.                AND     CX,7                    ; appropriate space characters.
  425.                NEG     CX
  426.                ADD     CX,8
  427. NEXT_TAB:      PUSH    CX
  428.                MOV     AL,SPACE
  429.                CALL    CK_ACTIVE
  430.                POP     CX
  431.                LOOP    NEXT_TAB
  432.                RET
  433.  
  434. CK_ACTIVE:     TEST    STATUS,OFF              ;Is ANSI OFF?
  435.                JNZ     WRITE_TTY               ;If yes, write via BIOS TTY.
  436. CK_FAST:       CALL    CK_SLOW_TEXT            ;Is ANSI SLOW or in graphics
  437.                JNC     WRITE_FAST              ; mode? If no, write fast.
  438.  
  439.                CMP     AL,CR                   ;Let BIOS handle CR and LF.
  440.                JZ      WRITE_TTY
  441.                CMP     AL,LF
  442.                JZ      WRITE_TTY
  443.  
  444. WRITE_SLOW:    PUSH    AX                      ;Else, write character/attribute
  445.                MOV     CX,1                    ; at current cursor position
  446.                MOV     BH,ACTIVE_PAGE          ; via BIOS.
  447.                MOV     BL,ATTRIBUTE
  448.                MOV     AH,9
  449.                INT     10H
  450.                POP     AX
  451.  
  452.                CMP     LINE_WRAP,ON            ;Is line wrap on?
  453.                JZ      TTY                     ;If yes, continue.
  454.                MOV     CX,CRT_COLS             ;Else, cursor at rightmost
  455.                DEC     CL                      ; column?
  456.                CMP     CL,CURSOR_COL           ;If yes, continue, else
  457.                JNZ     TTY                     ; return without writing.
  458.                RET
  459.  
  460. ;------------------------------------------------;
  461.  
  462. WRITE_TTY:     MOV     BL,7                    ;Attribute in graphics mode.
  463. TTY:           MOV     AH,0EH
  464.                INT     10H
  465.                RET
  466.  
  467. ;----------------------------------------------------------------------------;
  468. ; Fast screen writes are directly to the video buffer without retrace check. ;
  469. ;----------------------------------------------------------------------------;
  470.  
  471. WRITE_FAST:    PUSH    ES                      ;Preserve extra segment.
  472.                MOV     DX,CURSOR_POSN          ;Retrieve cursor position.
  473.                CMP     AL,CR                   ;Carriage return?
  474.                JNZ     CK_LINEFEED             ;If no, check linefeed.
  475.                XOR     DL,DL                   ;Else, cursor to first column.
  476.                JMP     SHORT UPDATE_CURSOR
  477. CK_LINEFEED:   CALL    VIDEO_SETUP             ;Calculate video address.
  478.                CMP     AL,LF                   ;Linefeed?
  479.                JZ      NEXT_ROW                ;If yes, next row.
  480.  
  481.                MOV     AH,ATTRIBUTE            ;Retrieve attribute.
  482.                STOSW                           ;Put char/attrib in video buffer.
  483.                INC     DL                      ;Increment cursor column.
  484.                CMP     DL,BYTE PTR CRT_COLS    ;End of row?
  485.                JB      UPDATE_CURSOR           ;If no, update cursor.
  486.  
  487.                CMP     LINE_WRAP,OFF           ;Else, line wrap off?
  488.                JZ      FAST_END                ;If yes, don't move cursor.
  489.                XOR     DL,DL                   ;Else, column zero.
  490. NEXT_ROW:      INC     DH                      ;Next row.
  491.                CALL    INFORMATION             ;Get displayable row info.
  492.                CMP     DH,AL                   ;Beyond the bottom of screen?
  493.                JBE     UPDATE_CURSOR           ;If no, update cursor.
  494.  
  495.                DEC     DH                      ;Else, cursor to original row.
  496.                MOV     DI,CRT_START            ;Point destination to top.
  497.                MOV     SI,DI                   ;Point source to second row
  498.                MOV     AX,CRT_COLS             ; by adding width in columns
  499.                PUSH    AX                      ; twice for char/attribute.
  500.                ADD     SI,AX
  501.                ADD     SI,AX
  502.                MUL     DH                      ;Times displayable rows - 1.
  503.                MOV     CX,AX                   ; equals char/attrib to scroll.
  504.                PUSH    DS                      ;Save data segment and
  505.                PUSH    ES                      ; point to video segment.
  506.                POP     DS
  507.                REP     MOVSW                   ;Scroll the screen.
  508.                POP     DS                      ;Restore data segment.
  509.                POP     CX                      ;Retrieve CRT columns.
  510.                MOV     AL,SPACE                ;Write space/attrib to
  511.                MOV     AH,ATTRIBUTE            ; bottom row.
  512.                REP     STOSW
  513.  
  514. UPDATE_CURSOR: CALL    SET_CURSOR              ;Update the cursor position.
  515. FAST_END:      POP     ES                      ;Restore extra segment.
  516.                RET
  517.  
  518. ;************* SUPPORT ROUTINES *************;
  519.  
  520. CK_QUOTE:      MOV     BX,OFFSET QUOTE_STATE   ;Assume quote state.
  521.                MOV     AH,DOUBLE_QUOTE
  522.                CMP     AL,AH                   ;Is it double quote?
  523.                JZ      GOT_QUOTE               ;If yes, string delimiter.
  524.                MOV     AH,SINGLE_QUOTE         ;Is it single quote?
  525.                CMP     AL,AH                   ;Is yes, string delimiter.
  526.                JNZ     QUOTE_END               ;Else, return ZR = 0.
  527. GOT_QUOTE:     MOV     QUOTE_TYPE,AH           ;Store as matching string end.
  528. QUOTE_END:     RET
  529.  
  530. ;------------------------------------------------;
  531.  
  532. ACCUMULATE:    PUSH    AX                      ;Preserve number character.
  533.                SUB     AL,"0"                  ;Convert ASCII to binary.
  534.                MOV     CL,AL                   ;Save the number.
  535.                MOV     AX,10                   ;Multiply previous count by 10.
  536.                MOV     BX,NUMBER_COUNT
  537.                MUL     BYTE PTR NUMBER_BUFFER[BX]
  538.                ADD     AL,CL                            ;Add in new number
  539.                MOV     BYTE PTR NUMBER_BUFFER[BX],AL    ; and store.
  540.                POP     AX
  541.                RET
  542.  
  543. ;---------------------------------------------------------------------------;
  544. ; OUTPUT:  CY = 1 if write SLOW mode or in graphics mode; CY = 0 otherwise. ;
  545. ;---------------------------------------------------------------------------;
  546.  
  547. CK_SLOW_TEXT:  TEST    STATUS,SLOW
  548.                JNZ     SLOW_MODE
  549.                CMP     CRT_MODE,7
  550.                JZ      TEXT_MODE
  551.                CMP     CRT_MODE,3
  552.                JA      SLOW_MODE
  553. TEXT_MODE:     CLC
  554.                RET
  555.  
  556. SLOW_MODE:     STC
  557.                RET
  558.  
  559. ;-------------------------------------;
  560. ; OUTPUT:  AL = Screen rows minus one ;
  561. ;-------------------------------------;
  562.  
  563. INFORMATION:   PUSH    DS                      ;Save data segment.
  564.                MOV     AX,40H                  ;Point to BIOS data.
  565.                MOV     DS,AX
  566.                MOV     AL,DS:[84H]             ;Retrieve rows - 1.
  567.                OR      AL,AL                   ;BIOS supported?
  568.                JNZ     INFO_END                ;If yes, done here.
  569.                MOV     AL,24                   ;Else, assume 25 lines.
  570. INFO_END:      POP     DS
  571.                RET
  572.  
  573. ;------------------------------------------------------------------------------;
  574. ; INPUT:  DX = Cursor position.                                                ;
  575. ; OUTPUT: ES = Video buffer segment; DI = Video buffer offset; AX,DX preserved ;
  576. ;------------------------------------------------------------------------------;
  577.  
  578. VIDEO_SETUP:   PUSH    AX
  579.                MOV     AX,CRT_COLS             ;Retrieve CRT columns.
  580.                MUL     DH                      ;Times cursor row.
  581.                MOV     BL,DL
  582.                XOR     BH,BH
  583.                ADD     AX,BX                   ;Plus cursor column.
  584.                SHL     AX,1                    ;Times two for attribute.
  585.                MOV     DI,CRT_START            ;Plus starting video offset.
  586.                ADD     DI,AX                   ;Equals destination.
  587.  
  588.                MOV     BX,0B000H               ;Assume mono card.
  589.                CMP     ADDR_6845,3B4H          ;Is it mono port?
  590.                JZ      VIDEO_SEGMENT           ;If yes, guessed right.
  591.                ADD     BX,800H                 ;Else, point to color segment.
  592. VIDEO_SEGMENT: MOV     ES,BX
  593.                POP     AX
  594.                RET
  595.  
  596. ;------------------------------------------------;
  597. ; Move BIOS video data into our data segment.    ;
  598. ;------------------------------------------------;
  599.  
  600. GET_BIOS_DATA: PUSH    DS
  601.                PUSH    DI                      ;Point to BIOS data segment.
  602.                MOV     BX,40H
  603.                MOV     DS,BX
  604.                MOV     SI,BIOS_ACTIVE_PAGE     ;Start with active page.
  605.                MOV     DI,OFFSET ACTIVE_PAGE
  606.                MOVSB                           ;Retrieve active page
  607.                MOVSW                           ; and address of 6845 port.
  608.                MOV     SI,BIOS_CRT_MODE        ;Retrieve CRT mode, CRT columns,
  609.                MOV     CX,CRT_DATA_LENGTH      ; CRT length, CRT start.
  610.                REP     MOVSB
  611.                MOV     BL,ES:ACTIVE_PAGE       ;Use active page as index
  612.                XOR     BH,BH                   ; of active cursor position.
  613.                SHL     BX,1
  614.                ADD     SI,BX
  615.                MOVSW
  616.                POP     DI
  617.                POP     DS
  618.                RET
  619.  
  620. ;----------------------------------------------------------------------------;
  621. ; OUTPUT: BL = First parameter; BH = Second parameter; DX = cursor position. ;
  622. ;----------------------------------------------------------------------------;
  623.  
  624. ADJUST_NUMBER: MOV     BX,WORD PTR NUMBER_BUFFER
  625.                OR      BH,BH
  626.                JNZ     ADJUST_AL               ;If either parameter zero,
  627.                INC     BH                      ; increment to convert
  628. ADJUST_AL:     OR      BL,BL                   ; to base one.
  629.                JNZ     ADJUST_END
  630.                INC     BL
  631. ADJUST_END:    MOV     DX,CURSOR_POSN          ;Return cursor position.
  632.                RET
  633.  
  634. ;--------------------------------------------------------------------------;
  635. ; INPUT:  AX = number; BP = 0 if console output; BP = 1 if storage output. ;
  636. ;--------------------------------------------------------------------------;
  637.  
  638. DECIMAL_OUT:   MOV     BX,10                   ;Divisor of ten.
  639.                XOR     CX,CX                   ;Zero in counter.
  640. NEXT_COUNT:    XOR     DX,DX                   ;Zero in high half.
  641.                DIV     BX                      ;Divide by ten.
  642.                ADD     DL,"0"                  ;Convert to ASCII.
  643.                PUSH    DX                      ;Save results.
  644.                INC     CX                      ;Also increment count.
  645.                CMP     AX,0                    ;Are we done?
  646.                JNZ     NEXT_COUNT              ;Continue until zero.
  647.                OR      BP,BP                   ;If BP zero, output to screen.
  648.                JNZ     DEVICE_OUTPUT           ;Else, store in buffer.
  649.  
  650. REPORT_OUTPUT: POP     AX                      ;Retrieve number.
  651.                CALL    PRINT_CHAR              ;Display it.
  652.                LOOP    REPORT_OUTPUT
  653.                RET
  654.  
  655. DEVICE_OUTPUT: CMP     CX,2                    ;Two digits?
  656.                JZ      TWO_DIGITS              ;If yes, process normally.
  657.                MOV     AL,"0"                  ;Else, store leading zero.
  658.                STOSB
  659.  
  660. TWO_DIGITS:    POP     AX                      ;Retrieve number.
  661.                STOSB                           ;Store it
  662.                LOOP    TWO_DIGITS
  663.                RET
  664.  
  665. ;************* ANSI COMMAND SUBSET *************;
  666.  
  667. CLS:           MOV     BH,7                    ;Assume normal attribute.
  668.                MOV     BL,BYTE PTR CRT_MODE    ;Get current video mode.
  669.                CMP     BL,4
  670.                JBE     CK_CLS_STATUS           ;If text mode then check if
  671.                CMP     BL,7                    ; ANSI active.
  672.                JZ      CK_CLS_STATUS
  673.                XOR     BH,BH                   ;Else, use black attribute
  674.                TEST    STATUS,OFF
  675.                JNZ     CLS_SLOW                ;If ANSI inactive, via BIOS.
  676.                JMP     SHORT CK_CLS_SLOW       ;Else, check if FAST or SLOW.
  677.  
  678. CK_CLS_STATUS: TEST    STATUS,OFF              ;Is ANSI OFF?
  679.                JNZ     CLS_SLOW                ;If yes, CLS via BIOS.
  680.                MOV     BH,ATTRIBUTE            ;Else, current attribute.
  681. CK_CLS_SLOW:   CALL    CK_SLOW_TEXT
  682.                JC      CLS_SLOW                ;If ANSI SLOW, via BIOS.
  683.  
  684. CLS_FAST:      MOV     AH,BH                   ;Else, attribute in high half.
  685.                MOV     AL,SPACE                ;Space character in low half.
  686.                XOR     DX,DX                   ;Cursor position home.
  687.                CALL    VIDEO_SETUP             ;Calculate video address.
  688.                MOV     CX,CRT_LEN              ;Retrieve CRT length.
  689.                SHR     CX,1                    ;Times two for attribute.
  690.                REP     STOSW                   ;Write directly to video buffer.
  691.                JMP     SHORT SET_CURSOR        ;Set the cursor top left corner.
  692.  
  693. CLS_SLOW:      CALL    INFORMATION             ;Get displayable rows.
  694.                MOV     DH,AL                   ;Store in DH.
  695.                MOV     DL,BYTE PTR CRT_COLS    ;Retrieve CRT columns.
  696.                DEC     DL                      ;Adjust to zero base.
  697.                XOR     CX,CX                   ;Scroll active page.
  698.                MOV     AX,600H
  699.                INT     10H
  700.                XOR     DX,DX                   ;Home the cursor.
  701.                JMP     SHORT SET_CURSOR
  702.  
  703. ;------------------------------------------------;
  704. CURS_POSITION:                                 ;These two commands are the same.
  705. HORZ_VERT_POS: CALL    INFORMATION             ;Returns AL = screen rows.
  706.                CALL    ADJUST_NUMBER           ;Returns BL,BH = parameters.
  707.                SUB     BX,101H                 ;Zero based.
  708.                CMP     BL,AL                   ;If cursor request within
  709.                JA      CURSOR_END              ; boundaries of screen, set it.
  710.                CMP     BH,BYTE PTR CRT_COLS
  711.                JAE     CURSOR_END
  712.                XCHG    BH,BL
  713.                MOV     DX,BX
  714.  
  715. SET_CURSOR:    MOV     BH,ACTIVE_PAGE          ;Set cursor via BIOS.
  716.                MOV     AH,2
  717.                INT     10H
  718. CURSOR_END:    RET
  719.  
  720. ;------------------------------------------------;
  721.  
  722. CURSOR_UP:     CALL    ADJUST_NUMBER           ;Move cursor up one or more rows
  723.                OR      DH,DH                   ; without changing column.
  724.                JZ      CURSOR_END              ;If already at top, ignore.
  725.                SUB     DH,BL
  726.                JNC     SET_CURSOR              ;Stay in bounds.
  727.                XOR     DH,DH
  728.                JMP     SHORT SET_CURSOR
  729.  
  730. ;------------------------------------------------;
  731.  
  732. CURSOR_DOWN:   CALL    INFORMATION             ;Returns AL = screen rows.
  733.                CALL    ADJUST_NUMBER           ;Returns BL,BH =nos.; DX = cursor
  734.                CMP     DH,AL                   ;Move cursor down requested
  735.                JZ      CURSOR_END              ; rows without changing column.
  736.                ADD     DH,BL
  737.                CMP     DH,AL
  738.                JNA     SET_CURSOR
  739.                MOV     DH,AL                   ;Stay in bounds.
  740.                JMP     SHORT SET_CURSOR
  741.  
  742. ;------------------------------------------------;
  743.  
  744. CURS_FORWARD:  CALL    ADJUST_NUMBER           ;Returns BL,BH =nos.; DX = cursor
  745.                MOV     BH,BYTE PTR CRT_COLS    ;Retrieve displayable columns.
  746.                DEC     BH                      ;Adjust to zero base.
  747.                CMP     DL,BH                   ;Move cursor forward one or more
  748.                JZ      CURSOR_END              ; columns without changing row.
  749.                ADD     DL,BL
  750.                CMP     DL,BH
  751.                JNA     SET_CURSOR
  752.                MOV     DL,BH                   ;Stay in bounds.
  753.                JMP     SHORT SET_CURSOR
  754.  
  755. ;------------------------------------------------;
  756.  
  757. CURS_BACKWARD: CALL    ADJUST_NUMBER           ;Returns BL,BH =nos.; DX = cursor
  758.                OR      DL,DL                   ;Move cursor backward one or
  759.                JZ      CURSOR_END              ; more columns without changing
  760.                SUB     DL,BL                   ; row.  Ignore if already left.
  761.                JNC     SET_CURSOR
  762.                XOR     DL,DL                   ;Stay in bounds.
  763.                JMP     SHORT SET_CURSOR
  764.  
  765. ;--------------------------------------------------------;
  766. ; Report cursor position via console; Format: ESC[#;#R   ;
  767. ;--------------------------------------------------------;
  768.  
  769. DEVICE_STATUS: MOV     DI,OFFSET DEVICE_STATUS_BUFFER
  770.                MOV     AL,ESC_CHAR
  771.                STOSB                           ;Store leading Esc, bracket
  772.                MOV     AL,"["                  ;in special Device Status Buffer.
  773.                STOSB
  774.  
  775.                MOV     AL,CURSOR_ROW           ;Retrieve cursor row.
  776.                XOR     AH,AH                   ;Zero in high half.
  777.                INC     AX                      ;Convert to base one.
  778.                MOV     BP,1                    ;Flag as storage.
  779.                CALL    DECIMAL_OUT             ;Convert to ASCII.
  780.                MOV     AL,";"                  ;Store delimiting semi-colon.
  781.                STOSB
  782.                MOV     AL,CURSOR_COL           ;Do same with cursor column.
  783.                XOR     AH,AH
  784.                INC     AX
  785.                CALL    DECIMAL_OUT
  786.                MOV     AL,"R"                  ;Add command character
  787.                STOSB
  788.                MOV     AL,CR                   ; and carriage return.
  789.                STOSB
  790.                SUB     DI,OFFSET DEVICE_STATUS_BUFFER   ;Setup for console out.
  791.                MOV     REASSIGN_COUNT,DI
  792.                MOV     REASSIGN_POS,OFFSET DEVICE_STATUS_BUFFER
  793.                MOV     REASSIGN_FLAG,ON
  794.                MOV     REMOVE_FLAG,OFF
  795.                RET
  796.  
  797. ;------------------------------------------------;
  798.  
  799. SAVE_CURSOR:   MOV     DX,CURSOR_POSN
  800.                MOV     SAVE_POSITION,DX
  801.                RET
  802.  
  803. ;------------------------------------------------;
  804.  
  805. RESTORE_CURS:  MOV     DX,SAVE_POSITION
  806.                JMP     SET_CURSOR
  807.  
  808. ;------------------------------------------------;
  809.  
  810. ERASE_IN_LINE: MOV     DX,CURSOR_POSN          ;Erase from the cursor to
  811.                MOV     CX,CRT_COLS             ; the end of the line, including
  812.                SUB     CL,DL                   ; the current cursor position.
  813.                CALL    CK_SLOW_TEXT
  814.                JC      ERASE_SLOW              ;If ANSI ON and not graphics,
  815.                CALL    VIDEO_SETUP             ; write directly to video buffer
  816.                MOV     AL,SPACE                ; with space/attribute.
  817.                MOV     AH,ATTRIBUTE
  818.                REP     STOSW
  819.                RET
  820.  
  821. ERASE_SLOW:    MOV     CX,DX                   ;Else, erase SLOW via
  822.                MOV     DL,BYTE PTR CRT_COLS    ; BIOS scroll active page.
  823.                DEC     DL
  824.                MOV     BH,ATTRIBUTE
  825.                MOV     AX,601H
  826.                INT     10H
  827.                RET
  828.  
  829. ;------------------------------------------------;
  830. ;            Set Graphics Rendition              ;
  831. ;------------------------------------------------;
  832.  
  833. SGR:           MOV     SI,OFFSET NUMBER_BUFFER     ;Point to number parameters.
  834. NEXT_SGR:      LODSB                               ;Retrieve parameter.
  835.                MOV     DI,OFFSET ATTRIBUTE_TABLE   ;Look up attribute in
  836.                MOV     CX,ATTRIBUTE_LENGTH         ; translation table.
  837.                REPNZ   SCASB
  838.                JNZ     SGR_LOOP
  839.  
  840.                MOV     DI,OFFSET ATTRIBUTE_END
  841.                SHL     CX,1
  842.                SUB     DI,CX
  843.                MOV     AX,[DI]
  844.                AND     ATTRIBUTE,AL            ;If match, AND with strip mask.
  845.                OR      ATTRIBUTE,AH            ;OR with add mask.
  846. SGR_LOOP:      DEC     NUMBER_COUNT            ;Do all parameters.
  847.                JNZ     NEXT_SGR
  848.                RET
  849.  
  850. ;------------------------------------------------;
  851.  
  852. SET_MODE:      MOV     AH,ON                   ;Assume command 7,
  853.                JMP     SHORT CK_WRAP           ; turn line wrap on.
  854.  
  855. ;------------------------------------------------;
  856.  
  857. RESET_MODE:    MOV     AH,OFF                     ;Assume turn line wrap off.
  858. CK_WRAP:       MOV     AL,BYTE PTR NUMBER_BUFFER  ;Retrieve number parameter.
  859.                CMP     AL,7                       ;Is it 7?
  860.                JZ      SET_WRAP                ;If yes, set wrap mode.
  861.                JB      DO_MODE                 ;1 - 6 valid modes.
  862.                CMP     AL,14                   ;14 - 16 valid modes.
  863.                JB      MODE_END
  864.                CMP     AL,19
  865.                JA      MODE_END                ;If above 16, illegal.
  866. DO_MODE:       XOR     AH,AH                   ;Else, set video mode
  867.                INT     10H                     ; to parameter.
  868.                RET
  869.  
  870. SET_WRAP:      MOV     LINE_WRAP,AH
  871. MODE_END:      RET
  872.  
  873. ;--------------------------------------------------------------------;
  874. ; Keyboard Key Reassignment:  First convert ASCII numbers to binary, ;
  875. ; parse out delimiting semi-colons, and quote string delimiters.     ;
  876. ;--------------------------------------------------------------------;
  877.  
  878. REASSIGNMENT:  MOV     CX,ESC_COUNT            ;Retrieve length of Esc sequence.
  879.                DEC     CX                          ;Adjust.
  880.                MOV     SI,OFFSET ESC_BUFFER + 2    ;Point past Esc, bracket.
  881.                MOV     DI,OFFSET PARSE_BUFFER      ;Point to parse storage.
  882. NEXT_STRING:   MOV     QUOTE_TYPE,0                ;Reset quote type.
  883. NEXT_NUM:      XOR     DL,DL                   ;Use DL to carry number; init = 0
  884.                XOR     BP,BP                   ;Use BP as number flag.
  885. NEXT_PARAM:    LODSB                           ;Retrieve a byte.
  886.                DEC     CX                      ;Decrement string length counter.
  887.                JZ      PARSE_END               ;If zero, done.
  888.                MOV     AH,QUOTE_TYPE           ;Else, retrieve quote type.
  889.                OR      AH,AH                   ;If zero, not in string.
  890.                JNZ     QUOTE_MODE              ;Else, go to string mode.
  891.                CALL    CK_QUOTE                ;Is character a quote?
  892.                JZ      NEXT_PARAM              ;If yes, ignore.
  893.                CMP     AL,";"                  ;Else, is it semi-colon?
  894.                JZ      STORE_NUMBER            ;If yes, number delimiter.
  895.  
  896.                SUB     AL,"0"                  ;Else, must be number; convert
  897.                MOV     DH,AL                   ; to binary; save in DH.
  898.                MOV     AX,10                   ;Multiply current total by ten.
  899.                MUL     DL
  900.                ADD     AL,DH                   ;Add new number.
  901.                MOV     DL,AL                   ;Save in DL.
  902.                MOV     BP,1                    ;Flag that number mode active.
  903.                JMP     SHORT NEXT_PARAM        ;Next parameter.
  904.  
  905. STORE_NUMBER:  OR      BP,BP                   ;If number mode flag not set then
  906.                JZ      NEXT_PARAM              ;semi-colon not prefaced with no.
  907.                MOV     AL,DL                   ;Else, store number.
  908.                STOSB
  909.                JMP     SHORT NEXT_NUM          ;Reset number carrying registers.
  910.  
  911. QUOTE_MODE:    CMP     AL,AH                   ;Is current char a string ending
  912.                JZ      NEXT_STRING             ; quote?  If yes, exit quote mode
  913.                STOSB                           ;Else, store char as literal.
  914.                JMP     SHORT NEXT_PARAM
  915.  
  916. ;----------------------------------------------------------------------------;
  917. ; Remove duplicate assignments if removal leaves room for new assignment.    ;
  918. ; If no duplicate and room, add new assignment, else flush buffer to screen. ;
  919. ;----------------------------------------------------------------------------;
  920.  
  921. PARSE_END:     OR      BP,BP                   ;Was last parameter a number?
  922.                JZ      CK_LENGTH               ;If no, skip.
  923.                MOV     AL,DL                   ;Else, store the last number.
  924.                STOSB
  925. CK_LENGTH:     SUB     DI,OFFSET PARSE_BUFFER - 2   ;Calculate string length
  926.                MOV     AX,DI                        ; including word for length.
  927.                MOV     BX,WORD PTR PARSE_BUFFER     ;BL=new first ASCII; BH=2nd.
  928.                MOV     CX,5                         ;String length has to be at
  929.                OR      BL,BL                   ;least word + 2 for old + new = 5
  930.                JZ      CK_LEGAL                ; for extended key reassignment.
  931.                DEC     CX                      ;And word + 1 for old + new = 4
  932. CK_LEGAL:      CMP     AX,CX                   ; for regular ASCII reassignment.
  933.                JB      ASSIGN_FLUSH            ;If not, illegal; flush buffer.
  934.                MOV     BP,BUFFER_END           ;BP to carry buffer end pointer.
  935.                CALL    CK_MATCH                ;Is char already reassigned?
  936.                JC      CK_ROOM                 ;If no, check room for new.
  937.  
  938. CK_REMOVE:     MOV     CX,DX                   ;REASSIGN_END - string end
  939.                SUB     CX,SI                   ; = bytes to move.
  940.                JZ      CK_ROOM                 ;If zero, last string.
  941.                SUB     DX,[DI]                 ;Is REASSIGN_END - old string
  942.                ADD     DX,AX                   ; + new string >
  943.                CMP     DX,BP                   ; BUFFER_END?
  944.                JA      ASSIGN_FLUSH            ;If yes, flush buffer to screen.
  945.                REP     MOVSB                   ;Else, move them down in buffer.
  946.                JMP     SHORT STORE_NEW
  947.  
  948. CK_ROOM:       ADD     DX,AX                   ;New string + current strings.
  949.                CMP     DX,BP                   ;Greater than buffer size?
  950.                JA      ASSIGN_FLUSH            ;If yes, flush new string.
  951. STORE_NEW:     MOV     SI,OFFSET PARSE_BUFFER  ;Else, room for new string.
  952.                STOSW                           ;Store string length first.
  953.                MOV     CX,AX                   ;Adjust counter.
  954.                DEC     CX
  955.                DEC     CX
  956.                REP     MOVSB                   ;Store the actual string.
  957.                MOV     REASSIGN_END,DI         ;Store new string end.
  958.                RET
  959.  
  960. ASSIGN_FLUSH:  MOV     AL,"p"                  ;If buffer full, flush string
  961.                JMP     FLUSH_BUFFER            ; including ending "p" command.
  962.  
  963. ;------------------------------------------------------------------------------;
  964. ; INPUT:  BL = first ASCII code to match; BH = extended if BL = 0              ;
  965. ; OUTPUT: CY = 1 if no match found; CY = 0 if match found.                     ;
  966. ;         DI points to string length of match; DI+2 points to start of string. ;
  967. ;         SI = end of string; DX = REASSIGN_END; BP = BUFFER_END               ;
  968. ;------------------------------------------------------------------------------;
  969.  
  970. CK_MATCH:      MOV     DX,REASSIGN_END                ;Current strings end.
  971.                MOV     SI,OFFSET REASSIGNMENT_BUFFER  ;Point to buffer.
  972. NEXT_MATCH:    MOV     DI,SI                          ;Current record.
  973.                CMP     DI,DX                   ;End of strings?
  974.                JAE     NO_MATCH                ;If yes, no match.
  975.                MOV     CX,[DI+2]               ;CL=current first ASCII; CH=2nd
  976.                ADD     SI,[DI]                 ;Point to next record.
  977.                CMP     BL,CL                   ;First characters match?
  978.                JNZ     NEXT_MATCH              ;If no, check next record.
  979.                OR      BL,BL                   ;Extended code? ie zero.
  980.                JNZ     MATCH                   ;If no, then match.
  981.                CMP     BH,CH                   ;Else, check second code.
  982.                JNZ     NEXT_MATCH              ;If not same, no match.
  983. MATCH:         CLC                             ;Else return with CY = 0
  984.                RET
  985.  
  986. NO_MATCH:      STC                             ;No match; CY = 1.
  987.                RET
  988.  
  989. ;----------------------------------------------------------------------;
  990. ; Buffer area will write over disposable data and initialization code. ;
  991. ;----------------------------------------------------------------------;
  992.  
  993. ESC_BUFFER            =       $
  994. NUMBER_BUFFER         =       ESC_BUFFER + ESC_BUFFER_SIZE
  995. PARSE_BUFFER          =       NUMBER_BUFFER
  996. DEVICE_STATUS_BUFFER  =       PARSE_BUFFER + ESC_BUFFER_SIZE
  997. DEVICE_STATUS_SIZE    =       11
  998. REASSIGNMENT_BUFFER   =       DEVICE_STATUS_BUFFER + DEVICE_STATUS_SIZE
  999.  
  1000. ;              DISPOSABLE DATA
  1001. ;              ---------------
  1002.  
  1003. SYNTAX         LABEL   BYTE
  1004. DB      "Syntax:  ANSI [FAST | SLOW][ON | OFF][/B nnn][/C][/U]",CR,LF
  1005. DB      "FAST = direct screen writes; default",CR,LF
  1006. DB      "SLOW = screen writes via BIOS",CR,LF
  1007. DB      "ON/OFF = active/inactive; default is ON",CR,LF
  1008. DB      "nnn = buffer size in bytes (0 - 60K) reserved for key reassignment; "
  1009. DB      "default 200",CR,LF
  1010. DB      "/U = Uninstall"
  1011. CR_LF   DB     CR,LF,LF,"$"
  1012.  
  1013. STATUS_MSG     DB      "Status: $"
  1014. BUFFER_MSG     DB      CR,LF,"Buffer size: $"
  1015. BYTES_FREE     DB      CR,LF,"Bytes free:  $"
  1016. ANSI_SYS_MSG   DB      "ANSI.SYS is installed so "
  1017. NOT_INSTALLED  DB      "ANSI.COM not installed",CR,LF,"$"
  1018. CON            DB      "CON"
  1019. CON_OFFSET     EQU     10
  1020.  
  1021. SIZE_MSG       DB      "Uninstall to change buffer size",CR,LF,LF,"$"
  1022. UNLOAD_MSG     DB      "ANSI can't be uninstalled",CR,LF
  1023.                DB      "Uninstall resident programs in reverse order",CR,LF,"$"
  1024. NOT_ENOUGH     DB      "Not enough memory",CR,LF,"$"
  1025. ALLOCATE_MSG   DB      "Memory allocation error",CR,LF,BELL,"$"
  1026. INSTALL_MSG    DB      "Installed",CR,LF,"$"
  1027. UNINSTALL_MSG  DB      "Uninstalled",CR,LF,"$"
  1028.  
  1029. ;--------------------------------------------------------------------;
  1030. ; Search memory for a copy of our code, to see if already installed. ;
  1031. ;--------------------------------------------------------------------;
  1032.  
  1033. INITIALIZE     PROC    NEAR
  1034.                CLD                             ;All string operations forward.
  1035.                MOV     BX,OFFSET START         ;Point to start of code.
  1036.                NOT     BYTE PTR [BX]           ;Change a byte so no false match.
  1037.                XOR     DX,DX                   ;Start at segment zero.
  1038.                MOV     AX,CS                   ;Store our segment in AX.
  1039. NEXT_PARAG:    INC     DX                      ;Next paragraph.
  1040.                MOV     ES,DX
  1041.                CMP     DX,AX                   ;Is it our segment?
  1042.                JZ      ANNOUNCE                ;If yes, search is done.
  1043.                MOV     SI,BX                   ;Else, point to our signature.
  1044.                MOV     DI,BX                   ; and offset of possible match.
  1045.                MOV     CX,16                   ;Check 16 bytes for match.
  1046.                REP     CMPSB
  1047.                JNZ     NEXT_PARAG              ;If no match, keep looking.
  1048.  
  1049. ;------------------------------------------------;
  1050.  
  1051. ANNOUNCE:      MOV     DX,OFFSET SIGNATURE     ;Display our signature.
  1052.                CALL    PRINT_STRING
  1053.                MOV     DX,OFFSET SYNTAX        ;And syntax.
  1054.                CALL    PRINT_STRING
  1055.                MOV     SI,81H                  ;Point to command line.
  1056. NEXT_CAP:      LODSB                           ;Capitalize parameters.
  1057.                CMP     AL,CR
  1058.                JZ      PARSE
  1059.                CMP     AL,"a"
  1060.                JB      NEXT_CAP
  1061.                CMP     AL,"z"
  1062.                JA      NEXT_CAP
  1063.                AND     BYTE PTR [SI - 1],5FH
  1064.                JMP     SHORT NEXT_CAP
  1065.  
  1066. ;------------------------------------------------;
  1067.  
  1068. PARSE:         MOV     SI,81H                  ;Point to command line again.
  1069. NEXT_PARA:     XOR     AX,AX                   ;Position in status parameters.
  1070.                MOV     BX,4                    ;Status parameters each 4 bytes.
  1071. NEXT_STATUS:   MOV     DI,OFFSET PARAMETERS    ;Point to "OFF ON  SLOWFAST"
  1072.                ADD     DI,AX                   ;Point to next parameter.
  1073.                ADD     AX,BX
  1074.                CMP     AX,LAST_PARAMETER       ;Check all four possible statuses
  1075.                JA      CK_SWITCHES
  1076.                PUSH    SI                      ;Save command line pointer.
  1077.                MOV     CX,2                    ;Check first two bytes for match.
  1078.                REP     CMPSB
  1079.                POP     SI                      ;Recover command line pointer.
  1080.                JNZ     NEXT_STATUS
  1081.  
  1082.                DIV     BL                      ;If match, divide offset by four.
  1083.                MOV     CL,1                    ;Set a bit to match offset.
  1084. NEXT_SHIFT:    SHL     CL,1
  1085.                DEC     AL
  1086.                JNZ     NEXT_SHIFT
  1087.                SHR     CL,1                    ;Adjust.
  1088.                MOV     AH,STATUS_MASK          ;Retrieve appropriate ON/OFF
  1089.                CMP     CL,ON                   ; or FAST/SLOW mask.
  1090.                JBE     ADD_STATUS
  1091.                ROL     AH,1
  1092.                ROL     AH,1
  1093.  
  1094. ADD_STATUS:    AND     ES:STATUS,AH            ;Mask off old status.
  1095.                OR      ES:STATUS,CL            ;Add new status.
  1096.                INC     SI                      ;Bump command line pointer.
  1097.                INC     SI
  1098.  
  1099. CK_SWITCHES:   LODSB                           ;Get a byte.
  1100.                CMP     AL,CR                   ;Is it carriage return?
  1101.                JZ      INSTALL                 ;If yes, done here.
  1102.                CMP     AL,"/"                  ;Is there a switch character?
  1103.                JNZ     NEXT_PARA               ;If no, keep looking.
  1104.                LODSB                           ;Else, get the switch character.
  1105.                CMP     AL,"B"                  ;Is it "B" ?
  1106.                JNZ     CK_C                    ;If no, check "C".
  1107.                CALL    CK_INSTALLED            ;Else, see if already installed.
  1108.                JZ      GET_SIZE                ;If no, get buffer request size.
  1109.                MOV     DX,OFFSET SIZE_MSG      ;Else, display error message.
  1110.                CALL    PRINT_STRING
  1111.                JMP     SHORT NEXT_PARA
  1112. GET_SIZE:      CALL    DECIMAL_INPUT           ;Get number parameter.
  1113.                CMP     BX,REASSIGNMENT_MAX     ;If greater than maximum, use
  1114.                JBE     STORE_BUFFER            ; maximum, else use requested
  1115.                MOV     BX,REASSIGNMENT_MAX     ; size.
  1116. STORE_BUFFER:  MOV     REASSIGNMENT_SIZE,BX
  1117.                ADD     BX,OFFSET REASSIGNMENT_BUFFER  ;Calculate end of buffer.
  1118.                MOV     BUFFER_END,BX
  1119.                JMP     SHORT NEXT_PARA
  1120.  
  1121. CK_C:          CMP     AL,"C"                  ;Is it "C" ?
  1122.                JNZ     CK_U                    ;If not, check "U".
  1123.                MOV     ES:REASSIGN_END,OFFSET REASSIGNMENT_BUFFER  ;Clear buffer
  1124. CK_U:          CMP     AL,"U"                  ;Is it "U" ?
  1125.                JZ      DO_U                    ;If yes, try to uninstall.
  1126.                JMP     NEXT_PARA               ;Else, next parmater.
  1127. DO_U:          CALL    CK_INSTALLED            ;Else, see if installed.
  1128.                MOV     DX,OFFSET NOT_INSTALLED ;If no, exit with error message.
  1129.                JZ      LILLY_PAD               ;Too far for short jump.
  1130.                JMP     UNINSTALL               ;Else, uninstall.
  1131.  
  1132. ;------------------------------------------------;
  1133.  
  1134. INSTALL:       CALL    STATUS_REPORT           ;Display status.
  1135.                CALL    CK_INSTALLED            ;Check if already installed.
  1136.                JZ      CK_AVAILABLE            ;If no, see if enough memory.
  1137.                OR      AL,AL                   ;Else, done.
  1138.                JMP     EXIT                    ;Exit with ERRORLEVEL = 0.
  1139.  
  1140. ;--------------------------------;
  1141. ; This is the install procedure. ;
  1142. ;--------------------------------;
  1143.  
  1144. CK_AVAILABLE:  MOV     BP,OFFSET REASSIGNMENT_BUFFER   ;TSR ends at end
  1145.                ADD     BP,REASSIGNMENT_SIZE            ; of reassignment buffer.
  1146.                ADD     BP,15                           ;Round up.
  1147.                CMP     BP,DS:[6]               ;Buffer > PSP bytes in segment?
  1148.                MOV     DX,OFFSET NOT_ENOUGH    ;If yes, exit without installing
  1149.                JA      MSG_EXIT                ; with message.
  1150.  
  1151.                MOV     AX,3529H                ;Get undocumented INT 29 vector.
  1152.                INT     21H
  1153.                MOV     SI,OFFSET CON           ;Does it point to ANSI.SYS?
  1154.                MOV     DI,CON_OFFSET           ;Check by looking for "CON"
  1155.                MOV     CX,3                    ; as device name.
  1156.                REP     CMPSB
  1157.                MOV     DX,OFFSET ANSI_SYS_MSG  ;Exit with error message if
  1158. LILLY_PAD:     JZ      MSG_EXIT                ;ANSI.SYS loaded.
  1159.  
  1160.                MOV     OLD_INT_29[0],BX        ;Else save old interrupt.
  1161.                MOV     OLD_INT_29[2],ES
  1162.                MOV     DX,OFFSET ANSI_INT_29   ;Install new interrupt.
  1163.                MOV     AX,2529H
  1164.                INT     21H
  1165.                MOV     AX,3516H                ;Get INT 16 vector.
  1166.                INT     21H
  1167.                MOV     OLD_INT_16[0],BX        ;Save old interrupt.
  1168.                MOV     OLD_INT_16[2],ES
  1169.                MOV     DX,OFFSET ANSI_INT_16   ;Install new interrupt.
  1170.                MOV     AX,2516H
  1171.                INT     21H
  1172.                MOV     AX,3521H                ;Get DOS 21h interrupt.
  1173.                INT     21H
  1174.                MOV     OLD_INT_21[0],BX        ;Save old interrupt.
  1175.                MOV     OLD_INT_21[2],ES
  1176.                MOV     DX,OFFSET ANSI_INT_21   ;Install new interrupt.
  1177.                MOV     AX,2521H
  1178.                INT     21H
  1179.  
  1180.                MOV     AX,DS:[2CH]             ;Get environment segment.
  1181.                MOV     ES,AX
  1182.                MOV     AH,49H                  ;Free up environment.
  1183.                INT     21H
  1184.  
  1185.                MOV     DX,OFFSET INSTALL_MSG   ;Display install message.
  1186.                CALL    PRINT_STRING
  1187.                CALL    FLUSH_END               ;Setup the number buffer.
  1188.                MOV     DX,BP                   ;Retrieve resident byte request.
  1189.                MOV     CL,4
  1190.                SHR     DX,CL                   ;Convert to paragraphs.
  1191.                MOV     AX,3100H                ;Return error code of zero.
  1192.                INT     21H                     ;Terminate but stay resident.
  1193.  
  1194. ;-------------------------------------------------------------------;
  1195. ; Exit.  Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
  1196. ;-------------------------------------------------------------------;
  1197.  
  1198. MSG_EXIT:      CALL    PRINT_STRING
  1199. ERROR_EXIT:    MOV     AL,1                    ;ERRORLEVEL = 1.
  1200. EXIT:          MOV     AH,4CH                  ;Terminate.
  1201.                INT     21H
  1202.  
  1203. ;---------------------------------------------------;
  1204. ; This subroutine uninstalls the resident ANSI.COM. ;
  1205. ;---------------------------------------------------;
  1206.  
  1207. UNINSTALL:     AND     ES:STATUS,NOT ON        ;Turn OFF ANSI, just in case
  1208.                OR      ES:STATUS,OFF           ; can't uninstall.
  1209.                MOV     CX,ES                   ;Save segment in CX.
  1210.                MOV     AX,3529H                ;Get interrupt 29h.
  1211.                INT     21H
  1212.                CMP     BX,OFFSET ANSI_INT_29   ;Has it been hooked by another?
  1213.                JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
  1214.                MOV     BX,ES
  1215.                CMP     BX,CX                   ;Is the segment vector same?
  1216.                JNZ     UNINSTALL_ERR           ;If no, exit with error message.
  1217.  
  1218.                MOV     AX,3516H                ;Get interrupt 16h.
  1219.                INT     21H
  1220.                CMP     BX,OFFSET ANSI_INT_16   ;Has it been hooked by another?
  1221.                JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
  1222.                MOV     BX,ES
  1223.                CMP     BX,CX                   ;Is the segment vector same?
  1224.                JNZ     UNINSTALL_ERR           ;If no, exit with error message.
  1225.  
  1226.                MOV     AX,3521H                ;Get interrupt 21h.
  1227.                INT     21H
  1228.                CMP     BX,OFFSET ANSI_INT_21   ;Has it been hooked by another?
  1229.                JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
  1230.                MOV     BX,ES
  1231.                CMP     BX,CX                   ;Is the segment vector same?
  1232.                JNZ     UNINSTALL_ERR           ;If no, exit with error message.
  1233.  
  1234.                MOV     AH,49H                  ;Return memory to system pool.
  1235.                INT     21H
  1236.                MOV     DX,OFFSET ALLOCATE_MSG
  1237.                JC      MSG_EXIT                ;Display message if problem.
  1238.  
  1239.                MOV     DX,ES:OLD_INT_29[0]     ;Restore old INT 29.
  1240.                MOV     DS,ES:OLD_INT_29[2]
  1241.                MOV     AX,2529H
  1242.                INT     21H
  1243.                MOV     DX,ES:OLD_INT_16[0]     ;Restore old INT 16.
  1244.                MOV     DS,ES:OLD_INT_16[2]
  1245.                MOV     AX,2516H
  1246.                INT     21H
  1247.                MOV     DX,ES:OLD_INT_21[0]     ;Restore old INT 21.
  1248.                MOV     DS,ES:OLD_INT_21[2]
  1249.                MOV     AX,2521H
  1250.                INT     21H
  1251.  
  1252.                PUSH    CS
  1253.                POP     DS                      ;Point to our data.
  1254.                MOV     DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
  1255.                CALL    PRINT_STRING
  1256.                OR      AL,AL                   ;Exit with ERRORLEVEL = 0.
  1257.                JMP     EXIT
  1258.  
  1259. UNINSTALL_ERR: MOV     ES,CX                   ;If error, display OFF status.
  1260.                CALL    STATUS_REPORT
  1261.                MOV     DX,OFFSET UNLOAD_MSG    ;And exit with error message.
  1262.                JMP     MSG_EXIT
  1263. INITIALIZE     ENDP
  1264.  
  1265. ;--------------------------------------------------;
  1266. ; INPUT:  SI points to parameter start.            ;
  1267. ; OUTPUT: SI points to parameter end; BX = number. ;
  1268. ;--------------------------------------------------;
  1269.  
  1270. DECIMAL_INPUT  PROC    NEAR
  1271.                XOR     BX,BX                   ;Start with zero as number.
  1272. NEXT_DECIMAL:  LODSB                           ;Get a character.
  1273.                CMP     AL,CR                   ;Carriage return?
  1274.                JZ      ADJUST_DEC              ;If yes, done here.
  1275.                CMP     AL,"/"                  ;Forward slash?
  1276.                JZ      ADJUST_DEC              ;If yes, done here.
  1277.                SUB     AL,"0"                  ;ASCII to binary.
  1278.                JC      NEXT_DECIMAL            ;If not between 0 and 9, skip.
  1279.                CMP     AL,9
  1280.                JA      NEXT_DECIMAL
  1281.                CBW                             ;Convert byte to word.
  1282.                XCHG    AX,BX                   ;Swap old and new number.
  1283.                MOV     CX,10                   ;Shift to left by multiplying
  1284.                MUL     CX                      ; last entry by ten.
  1285.                JC      DECIMAL_ERROR           ;If carry, too big.
  1286.                ADD     BX,AX                   ;Add new number and store in BX.
  1287.                JNC     NEXT_DECIMAL            ;If not carry, next number.
  1288. DECIMAL_ERROR: MOV     BX,-1                   ;Else, too big; return -1.
  1289.  
  1290. ADJUST_DEC:    DEC     SI                      ;Adjust pointer.
  1291.                RET
  1292. DECIMAL_INPUT  ENDP
  1293.  
  1294. ;-------------------------------------------------------;
  1295. ; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;
  1296. ;-------------------------------------------------------;
  1297.  
  1298. CK_INSTALLED:  MOV     AX,ES
  1299.                MOV     BX,CS
  1300.                CMP     AX,BX                   ;Compare segments.
  1301.                RET
  1302.  
  1303. ;------------------------------------------------;
  1304.  
  1305. STATUS_REPORT: MOV     DX,OFFSET STATUS_MSG
  1306.                CALL    PRINT_STRING            ;Display "Status: ".
  1307.                MOV     BL,ES:STATUS
  1308.                MOV     BH,1
  1309. NEXT_REPORT:   TEST    BL,BH
  1310.                JZ      LOOP_STATUS
  1311.  
  1312.                MOV     DL,BH
  1313.                MOV     AX,-1
  1314. NEXT_BIT:      INC     AX
  1315.                SHR     DL,1
  1316.                JNC     NEXT_BIT
  1317.                SHL     AX,1
  1318.                SHL     AX,1
  1319.                MOV     SI,OFFSET PARAMETERS    ;Display appropriate ON or OFF,
  1320.                ADD     SI,AX                   ; SLOW or FAST.
  1321.                MOV     CX,4
  1322. REPORT:        LODSB
  1323.                CALL    PRINT_CHAR
  1324.                LOOP    REPORT
  1325.  
  1326. LOOP_STATUS:   SHL     BH,1
  1327.                CMP     BH,FAST
  1328.                JBE     NEXT_REPORT
  1329.                MOV     DX,OFFSET BUFFER_MSG    ;Display "Buffer size: ".
  1330.                CALL    PRINT_STRING
  1331.                MOV     AX,ES:REASSIGNMENT_SIZE
  1332.                PUSH    AX
  1333.                XOR     BP,BP
  1334.                CALL    DECIMAL_OUT             ;Display the size.
  1335.                MOV     DX,OFFSET BYTES_FREE    ;Display "Bytes free: ".
  1336.                CALL    PRINT_STRING
  1337.                POP     AX
  1338.                ADD     AX,OFFSET REASSIGNMENT_BUFFER
  1339.                SUB     AX,ES:REASSIGN_END
  1340.                CALL    DECIMAL_OUT             ;Display the bytes free.
  1341.                MOV     DX,OFFSET CR_LF
  1342.                CALL    PRINT_STRING
  1343.                RET
  1344.  
  1345. ;------------------------------------------------;
  1346.  
  1347. PRINT_CHAR:    MOV     DL,AL
  1348.                MOV     AH,2                    ;Print character via DOS.
  1349.                JMP     SHORT DOS_INT
  1350.  
  1351. PRINT_STRING:  MOV     AH,9                    ;Print string via DOS.
  1352. DOS_INT:       INT     21H
  1353.                RET
  1354.  
  1355. _TEXT          ENDS
  1356.                END     START
  1357.